#include "../include/BaseLib.hpp"

/*发生错误就退出进程*/
#define ERROR_ABORT(fd,str) if( (fd) ) { perror( (str) );abort(); }
/*两个读写端*/
static int pipfd[2];    
/* 服务器运行标志*/
bool g_server_run = true;
/*信号处理函数,这个处理函数是将信号值写入到管道中，然后在主消息循环中读取并处理消息 */
/*并且由于对管道设置了非阻塞，只有等到消息写入完毕，主消息循环才会接收到可以读取消息的通知*/
void sig_handle(int sig);
/*将信号处理函数添加到对应信号中*/
int add_sig(int sig);

int main(void)
{
    int ret = 0,epoll_count = 0;
    sockaddr_in server_address;
    bzero(&server_address,sizeof(server_address));
    server_address.sin_family = AF_INET;
    inet_pton(AF_INET,BaseLib::g_server_address,&server_address.sin_addr);
    server_address.sin_port = htons(BaseLib::g_server_port); 

    int listen_fd = socket(AF_INET,SOCK_STREAM,0);
    ERROR_ABORT(listen_fd < 0,"socket");
    ret = bind(listen_fd,(sockaddr*)&server_address,sizeof(server_address));
    ERROR_ABORT(ret < 0,"bind");
    ret = listen(listen_fd,5);
    ERROR_ABORT(ret < 0,"listen");

    /*添加epoll事件*/
    epoll_event events[BaseLib::g_max_event_num];
    int epoll_fd = epoll_create(5);
    ERROR_ABORT(epoll_fd<0,"epoll_create");
    BaseLib::add_epoll_fd(epoll_fd,listen_fd);

    /*创建读写管道*/
    ret = socketpair(PF_UNIX,SOCK_STREAM,0,pipfd);  //使用socketpair创建读写管道，相对于使用pipe函数，该管道是全双工
    ERROR_ABORT(ret < 0,"socketpair");
    BaseLib::set_noblockint(pipfd[1]);              //设置写入端为非阻塞
    BaseLib::add_epoll_fd(epoll_fd,pipfd[0]);       //这个作为读取端，用于在主消息循环中处理消息

    /*设置一直信号，为其添加处理函数*/
    add_sig(SIGHUP);    //Linux用户退出信号
    add_sig(SIGCHLD);   //用于父进程捕获子进程的终止消息
    add_sig(SIGTERM);   //程序结束信号
    add_sig(SIGINT);    //终止用户写入信号

    /*主消息循环*/
    while(g_server_run)
    {
        epoll_count = epoll_wait(epoll_fd,events,BaseLib::g_max_event_num,0);   //阻塞IO
        if(epoll_count < 0 && errno != EINTR)
        {
            printf("epoll_wait error!\n");
            break;
        }
        /*遍历所有完成IO*/
        for(int i=0;i<epoll_count;++i)
        {
            int sockfd = events[i].data.fd; 
            if(sockfd == listen_fd)  /*listen_fd读就绪，说明有客户端进行连接*/
            {
                sockaddr_in client_address;
                socklen_t client_length;
                int connfd = accept(listen_fd,(sockaddr*)&client_address,&client_length);
                BaseLib::add_epoll_fd(epoll_fd,connfd);
            }else if(sockfd == pipfd[0] && (events[i].events & EPOLLIN))
            {
                int sig;
                char signals[1024];
                ret = recv(pipfd[0],signals,sizeof(signals),0);
                if(ret < 0) continue;
                else if(ret == 0) continue;
                else
                {
                    /*由于信号的大小都是一个字节，所以接下来需要按字节来处理信号*/
                    /*并且由于ret直接保存了获取到的字节数，我们可以根据此来遍历*/
                    for(int i=0;i<ret;++i)
                    {
                        switch (signals[i])
                        {
                        case SIGHUP:
                            continue;
                            break;
                        case SIGCHLD:
                            break;
                        case SIGTERM:
                            break;
                        case SIGINT:
                            g_server_run = false;
                            break;
                        default:
                            break;
                        }
                    }
                }
            }
            else
            {
                /*处理客户端数据，省略*/
            }
        }
    }
    printf("close server!\n");
    close(listen_fd);
    close(pipfd[0]);
    close(pipfd[1]);
    return 0;
}

void sig_handle(int sig)
{
    int save_errno = errno;
    int msg = sig;  
    int ret = send(pipfd[1],(char*)&msg,1,0);   //每个信号都只占一个字节大小，这里就是内部只截取一个字节大小的数据复制到缓冲区再复制到内核然后发送
    if(ret < 0) perror("send");
    errno = save_errno;
}

int add_sig(int sig)
{
    struct sigaction sa;
    memset(&sa,'\0',sizeof(sa));
    sa.sa_handler = sig_handle;
    sa.sa_flags = SA_RESTART;   /*该标志的含义就是当一个阻塞的慢速系统调用被信号打断的时候，系统将不会直接返回错误而是执行重启系统调用，并且errno = EINTR*/
    sigfillset(&sa.sa_mask);   /*不允许接受信号*/
    int ret = sigaction(sig,&sa,NULL);  //为信号添加信号处理函数
    if(ret < 0) perror("sigaction");
    return ret;
}